package com.xiyou.rmls.utils;

import com.google.common.collect.Lists;
import com.xiyou.rmls.constants.rule.RuleConstant;
import com.xiyou.rmls.model.dto.common.DrlInfoDto;
import com.xiyou.rmls.model.vo.guiderule.GuideRuleConditionInfoVo;
import com.xiyou.rmls.model.vo.guiderule.GuideRuleGroupOutInfoVo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * drools的工具类
 */
public class DroolsUtils {

    /**
     * 构建规则组的RHS语句
     *
     * @return
     * @param guideRuleGroupOutInfoVo 当前规则组中的结果信息
     * @param ruleName 规则名称
     * @param ruleNameList 规则组下的规则名称
     */
    public static String buildRuleGroupWhen(GuideRuleGroupOutInfoVo guideRuleGroupOutInfoVo, String ruleName,
                                            List<String> ruleNameList) {
        // 判断当前是什么逻辑
        String groupResultConditionType = guideRuleGroupOutInfoVo.getGroupResultConditionType();
        // 定义最终的返回结果
        String result = StringUtils.EMPTY;
        switch (groupResultConditionType) {
            case RuleConstant.HIT_NUM_TYPE:
                result = buildHitNumTypeWhenStr(guideRuleGroupOutInfoVo, ruleName);
                break;
            case RuleConstant.LOGIC_TYPE:
                result = buildLogicTypeWhenStr(guideRuleGroupOutInfoVo, ruleName, ruleNameList);
                break;
            default:
                throw new IllegalStateException("暂时不支持此规则组类型: " + groupResultConditionType);
        }
        return result;
    }

    /**
     * freemarker生成drl文件
     *
     * @param templatePath 模版路径
     * @param templateName 模版名称
     * @param inputParam 输入map
     * @return
     * @throws Exception
     */
    public static String initRuleTemplate(String templatePath,
                                          String templateName,
                                          Map<String, Object> inputParam) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
        //这是模板的编码
        cfg.setDefaultEncoding("UTF-8");
        // 设置freemarker的文件路径
        cfg.setClassForTemplateLoading(DroolsUtils.class, templatePath);
        //获取模板
        Template template = cfg.getTemplate(templateName);
        // 设置编码格式
        if (!Objects.isNull(template)) {
            template.setOutputEncoding("UTF-8");
        }
        // 声明输出形式 将其变为字符串
        StringWriter stringWriter = new StringWriter();
        //将模板与数据模型合并
        template.process(inputParam, stringWriter);
        // 返回对应的字符串
        return stringWriter.toString();
    }

    /**
     * 构建逻辑类型的when条件
     *
     * @param guideRuleGroupOutInfoVo
     * @param groupRuleName
     * @param ruleNameList
     */
    private static String buildLogicTypeWhenStr(GuideRuleGroupOutInfoVo guideRuleGroupOutInfoVo,
                                                String groupRuleName,
                                                List<String> ruleNameList) {
        List<String> conditionList = Lists.newArrayList();
        // 定义一个列表 用来存储map中的条件
        List<String> mapConditionList = Lists.newArrayList();
        mapConditionList.add(String.format("this[\"%s\"] != null", groupRuleName));
        // 1|2$(3|4)
        // ["1", "|", "2", "&", "(", "3", "|", "4", ")"]
        List<String> customText = guideRuleGroupOutInfoVo.getCustomText();
        // 开始遍历
        for (String text : customText) {
            switch (text) {
                // 左括号
                case RuleConstant.LEFT_BRACKET:
                    conditionList.add(RuleConstant.LEFT_BRACKET);
                    break;
                // 右括号
                case RuleConstant.RIGHT_BRACKET:
                    conditionList.add(RuleConstant.RIGHT_BRACKET);
                    break;
                // 或
                case RuleConstant.SINGLE_OR:
                    conditionList.add(RuleConstant.DOUBLE_OR);
                    break;
                // 与
                case RuleConstant.SINGLE_AND:
                    conditionList.add(RuleConstant.DOUBLE_AND);
                    break;
                default:
                    // 数字
                    if (NumberUtils.isNumber(text)) {
                        // 规则的名称
                        // 下标是从0开始的
                        String ruleName = ruleNameList.get(Integer.valueOf(text) - 1);
                        conditionList.add(String.format("eval(isInSet(this[\"%s\"], \"%s\")) == true", groupRuleName,
                                ruleName));
                    }
            }
        }
        mapConditionList.add(StringUtils.join(conditionList, RuleConstant.BLANK));
        // 返回最终的结果
        return StringUtils.join(mapConditionList, RuleConstant.BLANK_DOUBLE_AND);
    }

    /**
     * 构建命中数量的when条件
     *
     *  @param guideRuleGroupOutInfoVo
     * @param groupRuleName
     */
    private static String buildHitNumTypeWhenStr(GuideRuleGroupOutInfoVo guideRuleGroupOutInfoVo, String groupRuleName) {
        // 定义一个列表 用来存储map中的条件
        List<String> mapConditionList = Lists.newArrayList();
        // 最小值
        int minValue = guideRuleGroupOutInfoVo.getMinSize();
        // 最大值
        int maxValue = guideRuleGroupOutInfoVo.getMaxSize();
        // 表示当前的map中存在一个
        mapConditionList.add(String.format("this[\"%s\"] != null", groupRuleName));
        mapConditionList.add(String.format("eval(judgeSetSize(this[\"%s\"], %d, %d)) == true", groupRuleName, minValue,
                maxValue));
        // 最终的返回结果
        return StringUtils.join(mapConditionList, RuleConstant.BLANK_DOUBLE_AND);
    }

    /**
     * 根据字段类型 选择对应的输出
     *
     * @param fieldType 字段类型
     * @param fieldValue 字段值
     * @return
     */
    public static String formatFiledValueByType(String fieldType, String fieldValue) {
        // 定义返回值
        StringBuilder sb = new StringBuilder();
        // 根据类型判断怎么处理具体的数值
        switch (fieldType) {
            case RuleConstant.NUM:
                sb.append(fieldValue);
                break;
            case RuleConstant.TEXT:
                sb.append("\"");
                sb.append(fieldValue);
                sb.append("\"");
                break;
            case RuleConstant.DATE:
                sb.append(fieldValue);
                break;
            case RuleConstant.CODE:
                sb.append(fieldValue);
                break;
            default:
                throw new IllegalStateException("暂时不支持字段类型: " + fieldType);
        }
        return sb.toString();
    }

    /**
     * 组装then的条件（当页面配置了输出需要条件的时候才会触发）
     *
     * @param ruleConditionInfoVo
     * @return
     */
    public static String formatThenCondition(GuideRuleConditionInfoVo ruleConditionInfoVo) {
        // 得到字段的类型
        String fieldType = ruleConditionInfoVo.getFieldType();
        // 得到字段的code
        String fieldEn = ruleConditionInfoVo.getFieldEn();
        // 得到字段值
        String filedValue = formatFiledValueByType(fieldType, ruleConditionInfoVo.getFieldValue());
        // 定义返回值
        StringBuilder sb = new StringBuilder();
        // 先判断map中是否存在这个key
        sb.append("(");
        sb.append("$insertMap.containsKey(\"" + fieldEn + "\")");
        sb.append(" && ");
        // 根据类型判断怎么处理具体的数值
        switch (fieldType) {
            case RuleConstant.NUM:
                sb.append("(Integer)");
                sb.append("$insertMap.get(\"");
                sb.append(fieldEn);
                sb.append("\")");
                break;
            case RuleConstant.TEXT:
                sb.append("(String)");
                sb.append("$insertMap.get(\"");
                sb.append(fieldEn);
                sb.append("\")");
                break;
            case RuleConstant.DATE:
                sb.append("(Date)");
                sb.append("$insertMap.get(\"");
                sb.append(fieldEn);
                sb.append("\")");
                break;
            case RuleConstant.CODE:
                sb.append("$insertMap.get(\"");
                sb.append(fieldEn);
                sb.append("\")");
                break;
            default:
                throw new IllegalStateException("暂时不支持字段类型: " + fieldType);
        }
        sb.append(" ");
        sb.append(ruleConditionInfoVo.getOperator());
        sb.append(" ");
        sb.append(filedValue);
        sb.append(")");
        return sb.toString();
    }
}
